home *** CD-ROM | disk | FTP | other *** search
/ Aminet 45 / Aminet 45 (2001)(GTI - Schatztruhe)[!][Oct 2001].iso / Aminet / gfx / x11 / x3270_3_2_16.lha / unix_files / macros.c < prev    next >
C/C++ Source or Header  |  2009-02-26  |  53KB  |  2,471 lines

  1. /*
  2.  * Copyright 1993, 1994, 1995, 1996, 1999, 2000 by Paul Mattes.
  3.  *  Permission to use, copy, modify, and distribute this software and its
  4.  *  documentation for any purpose and without fee is hereby granted,
  5.  *  provided that the above copyright notice appear in all copies and that
  6.  *  both that copyright notice and this permission notice appear in
  7.  *  supporting documentation.
  8.  */
  9.  
  10. /*
  11.  *      macros.c
  12.  *              This module handles string, macro and script (sms) processing.
  13.  */
  14.  
  15. #include "globals.h"
  16.  
  17. #if defined(X3270_MENUS) /*[*/
  18. #include <X11/StringDefs.h>
  19. #include <X11/Xaw/Dialog.h>
  20. #endif /*]*/
  21.  
  22. #include <sys/wait.h>
  23. #include <sys/signal.h>
  24. #include <errno.h>
  25. #include <fcntl.h>
  26. #include <signal.h>
  27. #include <stdarg.h>
  28. #include "3270ds.h"
  29. #include "appres.h"
  30. #include "ctlr.h"
  31. #include "screen.h"
  32. #include "resources.h"
  33.  
  34. #include "actionsc.h"
  35. #include "ctlrc.h"
  36. #include "ftc.h"
  37. #include "hostc.h"
  38. #include "kybdc.h"
  39. #include "macrosc.h"
  40. #include "menubarc.h"
  41. #include "popupsc.h"
  42. #if defined(X3270_PRINTER) /*[*/
  43. #include "printerc.h"
  44. #endif /*]*/
  45. #include "screenc.h"
  46. #include "statusc.h"
  47. #include "tablesc.h"
  48. #include "trace_dsc.h"
  49. #include "utilc.h"
  50. #include "xioc.h"
  51.  
  52. #define ANSI_SAVE_SIZE    4096
  53.  
  54. /* Externals */
  55. extern int      linemode;
  56.  
  57. /* Globals */
  58. struct macro_def *macro_defs = (struct macro_def *)NULL;
  59. Boolean        macro_output = False;
  60.  
  61. /* Statics */
  62. typedef struct sms {
  63.     struct sms *next;    /* next sms on the stack */
  64.     char    msc[1024];    /* input buffer */
  65.     int    msc_len;    /* length of input buffer */
  66.     char   *dptr;        /* data pointer (macros only) */
  67.     enum sms_state {
  68.         SS_IDLE,    /* no command active (scripts only) */
  69.         SS_INCOMPLETE,    /* command(s) buffered and ready to run */
  70.         SS_RUNNING,    /* command executing */
  71.         SS_KBWAIT,    /* command awaiting keyboard unlock */
  72.         SS_CONNECT_WAIT,/* command awaiting connection to complete */
  73. #if defined(X3270_FT) /*[*/
  74.         SS_FT_WAIT,    /* command awaiting file transfer to complete */
  75. #endif /*]*/
  76.         SS_PAUSED,    /* stopped in PauseScript action */
  77.         SS_WAIT_ANSI,    /* awaiting completion of Wait(ansi) */
  78.         SS_WAIT_3270,    /* awaiting completion of Wait(3270) */
  79.         SS_WAIT_OUTPUT,    /* awaiting completion of Wait(Output) */
  80.         SS_SWAIT_OUTPUT,/* awaiting completion of Snap(Wait) */
  81.         SS_WAIT_DISC,    /* awaiting completion of Wait(Disconnect) */
  82.         SS_WAIT,    /* awaiting completion of Wait() */
  83.         SS_EXPECTING,    /* awaiting completion of Expect() */
  84.         SS_CLOSING    /* awaiting completion of Close() */
  85.     } state;
  86.     enum sms_type {
  87.         ST_STRING,    /* string */
  88.         ST_MACRO,    /* macro */
  89.         ST_COMMAND,    /* interactive command */
  90.         ST_KEYMAP,    /* keyboard map */
  91.         ST_CHILD,    /* child process */
  92.         ST_PEER        /* peer (external) process */
  93.     } type;
  94.     Boolean    success;
  95.     Boolean    need_prompt;
  96.     Boolean    is_login;
  97.     Boolean    is_hex;        /* flag for ST_STRING only */
  98.     Boolean output_wait_needed;
  99.     Boolean executing;    /* recursion avoidance */
  100.     FILE   *outfile;
  101.     int    infd;
  102.     int    pid;
  103.     unsigned long expect_id;
  104.     unsigned long wait_id;
  105. } sms_t;
  106. #define SN    ((sms_t *)NULL)
  107. static sms_t *sms = SN;
  108. static int sms_depth = 0;
  109.  
  110. #if defined(X3270_TRACE) /*[*/
  111. static const char *sms_state_name[] = {
  112.     "IDLE",
  113.     "INCOMPLETE",
  114.     "RUNNING",
  115.     "KBWAIT",
  116.     "CONNECT_WAIT",
  117. #if defined(X3270_FT) /*[*/
  118.     "FT_WAIT",
  119. #endif /*]*/
  120.     "PAUSED",
  121.     "WAIT_ANSI",
  122.     "WAIT_3270",
  123.     "WAIT_OUTPUT",
  124.     "SWAIT_OUTPUT",
  125.     "WAIT_DISC",
  126.     "WAIT",
  127.     "EXPECTING",
  128.     "CLOSING"
  129. };
  130. #endif /*]*/
  131.  
  132. #if defined(X3270_MENUS) /*[*/
  133. static struct macro_def *macro_last = (struct macro_def *) NULL;
  134. #endif /*]*/
  135. static unsigned long stdin_id = 0L;
  136. static unsigned char *ansi_save_buf;
  137. static int      ansi_save_cnt = 0;
  138. static int      ansi_save_ix = 0;
  139. static char    *expect_text = CN;
  140. static int    expect_len = 0;
  141. static const char *st_name[] = { "String", "Macro", "Command", "KeymapAction",
  142.                  "ChildScript", "PeerScript" };
  143. #define ST_sNAME(s)    st_name[(int)(s)->type]
  144. #define ST_NAME        ST_sNAME(sms)
  145.  
  146. static void script_prompt(Boolean success);
  147. static void script_input(void);
  148. static void sms_pop(Boolean can_exit);
  149. static void wait_timed_out(void);
  150.  
  151. /* Macro that defines that the keyboard is locked due to user input. */
  152. #define KBWAIT    (kybdlock & (KL_OIA_LOCKED|KL_OIA_TWAIT|KL_DEFERRED_UNLOCK))
  153.  
  154. /* Macro that defines when it's safe to continue a Wait()ing sms. */
  155. #define CAN_PROCEED ( \
  156.     IN_SSCP || \
  157.     (IN_3270 && formatted && cursor_addr && !KBWAIT) || \
  158.     (IN_ANSI && !(kybdlock & KL_AWAITING_FIRST)) \
  159. )
  160.  
  161. /* Callbacks for state changes. */
  162. static void
  163. sms_connect(Boolean connected)
  164. {
  165.     /* Hack to ensure that disconnects don't cause infinite recursion. */
  166.     if (sms != SN && sms->executing)
  167.         return;
  168.  
  169.     if (!connected) {
  170.         while (sms != SN && sms->is_login) {
  171.             if (sms->type == ST_CHILD && sms->pid > 0)
  172.                 (void) kill(sms->pid, SIGTERM);
  173.             sms_pop(False);
  174.         }
  175.     }
  176.     sms_continue();
  177. }
  178.  
  179. static void
  180. sms_in3270(Boolean in3270)
  181. {
  182.     if (in3270 || IN_SSCP)
  183.         sms_continue();
  184. }
  185.  
  186. /* One-time initialization. */
  187. void
  188. sms_init(void)
  189. {
  190.     register_schange(ST_CONNECT, sms_connect);
  191.     register_schange(ST_3270_MODE, sms_in3270);
  192. }
  193.  
  194. #if defined(X3270_MENUS) /*[*/
  195. /* Parse the macros resource into the macro list */
  196. void
  197. macros_init(void)
  198. {
  199.     char *s = CN;
  200.     char *name, *action;
  201.     struct macro_def *m;
  202.     int ns;
  203.     int ix = 1;
  204.     static char *last_s = CN;
  205.  
  206.     /* Free the previous macro definitions. */
  207.     while (macro_defs) {
  208.         m = macro_defs->next;
  209.         Free(macro_defs);
  210.         macro_defs = m;
  211.     }
  212.     macro_defs = (struct macro_def *)NULL;
  213.     macro_last = (struct macro_def *)NULL;
  214.     if (last_s) {
  215.         Free(last_s);
  216.         last_s = CN;
  217.     }
  218.  
  219.     /* Search for new ones. */
  220.     if (PCONNECTED) {
  221.         char *rname;
  222.         char *space;
  223.  
  224.         rname = NewString(current_host);
  225.         if ((space = strchr(rname, ' ')))
  226.             *space = '\0';
  227.         s = xs_buffer("%s.%s", ResMacros, rname);
  228.         Free(rname);
  229.         rname = s;
  230.         s = get_resource(rname);
  231.         Free(rname);
  232.     }
  233.     if (s == CN) {
  234.         if (appres.macros == CN)
  235.             return;
  236.         s = NewString(appres.macros);
  237.     } else
  238.         s = NewString(s);
  239.     last_s = s;
  240.  
  241.     while ((ns = split_dresource(&s, &name, &action)) == 1) {
  242.         m = (struct macro_def *)Malloc(sizeof(*m));
  243.         m->name = name;
  244.         m->action = action;
  245.         if (macro_last)
  246.             macro_last->next = m;
  247.         else
  248.             macro_defs = m;
  249.         m->next = (struct macro_def *)NULL;
  250.         macro_last = m;
  251.         ix++;
  252.     }
  253.     if (ns < 0) {
  254.         char buf[256];
  255.  
  256.         (void) sprintf(buf, "Error in macro %d", ix);
  257.         Warning(buf);
  258.     }
  259. }
  260. #endif /*]*/
  261.  
  262. /*
  263.  * Enable input from a script.
  264.  */
  265. static void
  266. script_enable(void)
  267. {
  268.     if (sms->infd >= 0 && stdin_id == 0) {
  269. #if defined(X3270_TRACE) /*[*/
  270.         if (toggled(DS_TRACE))
  271.             (void) fprintf(tracef, "Enabling input for %s[%d]\n",
  272.                 ST_NAME, sms_depth);
  273. #endif /*]*/
  274.         stdin_id = AddInput(sms->infd, script_input);
  275.     }
  276. }
  277.  
  278. /*
  279.  * Disable input from a script.
  280.  */
  281. static void
  282. script_disable(void)
  283. {
  284.     if (stdin_id != 0) {
  285. #if defined(X3270_TRACE) /*[*/
  286.         if (toggled(DS_TRACE))
  287.             (void) fprintf(tracef, "Disabling input for %s[%d]\n",
  288.                 ST_NAME, sms_depth);
  289. #endif /*]*/
  290.         RemoveInput(stdin_id);
  291.         stdin_id = 0L;
  292.     }
  293. }
  294.  
  295. /* Allocate a new sms. */
  296. static sms_t *
  297. new_sms(enum sms_type type)
  298. {
  299.     sms_t *s;
  300.  
  301.     s = (sms_t *)Calloc(1, sizeof(sms_t));
  302.  
  303.     s->state = SS_IDLE;
  304.     s->type = type;
  305.     s->dptr = s->msc;
  306.     s->success = True;
  307.     s->need_prompt = False;
  308.     s->is_login = False;
  309.     s->outfile = (FILE *)NULL;
  310.     s->infd = -1;
  311.     s->pid = -1;
  312.     s->expect_id = 0L;
  313.     s->wait_id = 0L;
  314.     s->output_wait_needed = False;
  315.     s->executing = False;
  316.  
  317.     return s;
  318. }
  319.  
  320. /*
  321.  * Push an sms definition on the stack.
  322.  * Returns whether or not that is legal.
  323.  */
  324. static Boolean
  325. sms_push(enum sms_type type)
  326. {
  327.     sms_t *s;
  328.  
  329.     /* Preempt any running sms. */
  330.     if (sms != SN) {
  331.         /* Remove the running sms's input. */
  332.         script_disable();
  333.     }
  334.  
  335.     s = new_sms(type);
  336.     if (sms != SN)
  337.         s->is_login = sms->is_login;    /* propagate from parent */
  338.     s->next = sms;
  339.     sms = s;
  340.  
  341.     /* Enable the abort button on the menu and the status indication. */
  342.     if (++sms_depth == 1) {
  343.         menubar_as_set(True);
  344.         status_script(True);
  345.     }
  346.  
  347.     if (ansi_save_buf == (unsigned char *)NULL)
  348.         ansi_save_buf = (unsigned char *)Malloc(ANSI_SAVE_SIZE);
  349.     return True;
  350. }
  351.  
  352. /*
  353.  * Add an sms definition to the _bottom_ of the stack.
  354.  */
  355. static sms_t *
  356. sms_enqueue(enum sms_type type)
  357. {
  358.     sms_t *s, *t, *t_prev = SN;
  359.  
  360.     /* Allocate and initialize a new structure. */
  361.     s = new_sms(type);
  362.  
  363.     /* Find the bottom of the stack. */
  364.     for (t = sms; t != SN; t = t->next)
  365.         t_prev = t;
  366.  
  367.     if (t_prev == SN) {    /* Empty stack. */
  368.         s->next = sms;
  369.         sms = s;
  370.  
  371.         /*
  372.          * Enable the abort button on the menu and the status
  373.          * line indication.
  374.          */
  375.         menubar_as_set(True);
  376.         status_script(True);
  377.     } else {            /* Add to bottom. */
  378.         s->next = SN;
  379.         t_prev->next = s;
  380.     }
  381.  
  382.     sms_depth++;
  383.  
  384.     if (ansi_save_buf == (unsigned char *)NULL)
  385.         ansi_save_buf = (unsigned char *)Malloc(ANSI_SAVE_SIZE);
  386.  
  387.     return s;
  388. }
  389.  
  390. /* Pop an sms definition off the stack. */
  391. static void
  392. sms_pop(Boolean can_exit)
  393. {
  394.     sms_t *s;
  395.  
  396. #if defined(X3270_TRACE) /*[*/
  397.     if (toggled(DS_TRACE))
  398.         (void) fprintf(tracef, "%s[%d] complete\n",
  399.             ST_NAME, sms_depth);
  400. #endif /*]*/
  401.  
  402.     /* When you pop the peer script, that's the end of x3270. */
  403.     if (sms->type == ST_PEER && can_exit)
  404.         x3270_exit(0);
  405.  
  406.     /* Remove the input event. */
  407.     script_disable();
  408.  
  409.     /* Close the files. */
  410.     if (sms->type == ST_CHILD) {
  411.         (void) fclose(sms->outfile);
  412.         (void) close(sms->infd);
  413.     }
  414.  
  415.     /* Cancel any pending timeouts. */
  416.     if (sms->expect_id != 0L)
  417.         RemoveTimeOut(sms->expect_id);
  418.     if (sms->wait_id != 0L)
  419.         RemoveTimeOut(sms->wait_id);
  420.  
  421.     /* Release the memory. */
  422.     s = sms;
  423.     sms = s->next;
  424.     Free((char *)s);
  425.     sms_depth--;
  426.  
  427.     if (sms == SN) {
  428.         /* Turn off the menu option. */
  429.         menubar_as_set(False);
  430.         status_script(False);
  431.     } else if (KBWAIT && (int)sms->state < (int)SS_KBWAIT) {
  432.         /* The child implicitly blocked the parent. */
  433.         sms->state = SS_KBWAIT;
  434. #if defined(X3270_TRACE) /*[*/
  435.         if (toggled(DS_TRACE))
  436.             (void) fprintf(tracef, "%s[%d] implicitly paused %s\n",
  437.                 ST_NAME, sms_depth, sms_state_name[sms->state]);
  438. #endif /*]*/
  439.     } else if (sms->state == SS_IDLE) {
  440.         /* The parent needs to be restarted. */
  441.         script_enable();
  442.     }
  443. }
  444.  
  445. /*
  446.  * Peer script initialization.
  447.  *
  448.  * Must be called after the initial call to connect to the host from the
  449.  * command line, so that the initial state can be set properly.
  450.  */
  451. void
  452. peer_script_init(void)
  453. {
  454.     sms_t *s;
  455.     Boolean on_top;
  456.  
  457.     if (!appres.scripted)
  458.         return;
  459.  
  460.     if (sms == SN) {
  461.         /* No login script running, simply push a new sms. */
  462.         (void) sms_push(ST_PEER);
  463.         s = sms;
  464.         on_top = True;
  465.     } else {
  466.         /* Login script already running, pretend we started it. */
  467.         s = sms_enqueue(ST_PEER);
  468.         s->state = SS_RUNNING;
  469.         on_top = False;
  470.     }
  471.  
  472.     s->infd = fileno(stdin);
  473.     s->outfile = stdout;
  474.     (void) SETLINEBUF(s->outfile);    /* even if it's a pipe */
  475.  
  476.     if (on_top) {
  477.         if (HALF_CONNECTED ||
  478.             (CONNECTED && (kybdlock & KL_AWAITING_FIRST)))
  479.             s->state = SS_CONNECT_WAIT;
  480.         else
  481.             script_enable();
  482.     }
  483. }
  484.  
  485.  
  486. /*
  487.  * Interpret and execute a script or macro command.
  488.  */
  489. enum em_stat { EM_CONTINUE, EM_PAUSE, EM_ERROR };
  490. static enum em_stat
  491. execute_command(enum iaction cause, char *s, char **np)
  492. {
  493.     enum {
  494.         ME_GND,        /* before action name */
  495.         ME_FUNCTION,    /* within action name */
  496.         ME_FUNCTIONx,    /* saw whitespace after action name */
  497.         ME_LPAREN,    /* saw left paren */
  498.         ME_P_PARM,    /* paren: within unquoted parameter */
  499.         ME_P_QPARM,    /* paren: within quoted parameter */
  500.         ME_P_BSL,    /* paren: after backslash in quoted parameter */
  501.         ME_P_PARMx,    /* paren: saw whitespace after parameter */
  502.         ME_S_PARM,    /* space: within unquoted parameter */
  503.         ME_S_QPARM,    /* space: within quoted parameter */
  504.         ME_S_BSL,    /* space: after backslash in quoted parameter */
  505.         ME_S_PARMx    /* space: saw whitespace after parameter */
  506.     } state = ME_GND;
  507.     char c;
  508.     char aname[64+1];
  509.     char parm[1024+1];
  510.     int nx = 0;
  511.     Cardinal count = 0;
  512.     String params[64];
  513.     int i, any, exact;
  514.     int failreason = 0;
  515.     Boolean saw_paren = False;
  516.     static const char *fail_text[] = {
  517.         /*1*/ "Action name must begin with an alphanumeric character",
  518.         /*2*/ "Syntax error in action name",
  519.         /*3*/ "Syntax error, \")\" expected",
  520.         /*4*/ "Extra data after parameters"
  521.     };
  522. #define fail(n) { failreason = n; goto failure; }
  523. #define free_params() { \
  524.     if (cause == IA_MACRO || cause == IA_KEYMAP || cause == IA_COMMAND) { \
  525.         Cardinal j; \
  526.         for (j = 0; j < count; j++) \
  527.             Free(params[j]); \
  528.     } \
  529. }
  530.  
  531.     parm[0] = '\0';
  532.     params[count] = parm;
  533.  
  534.     while ((c = *s++)) switch (state) {
  535.         case ME_GND:
  536.         if (isspace(c))
  537.             continue;
  538.         else if (isalnum(c)) {
  539.             state = ME_FUNCTION;
  540.             nx = 0;
  541.             aname[nx++] = c;
  542.         } else
  543.             fail(1);
  544.         break;
  545.         case ME_FUNCTION:    /* within function name */
  546.         if (c == '(' || isspace(c)) {
  547.             aname[nx] = '\0';
  548.             if (c == '(') {
  549.                 nx = 0;
  550.                 state = ME_LPAREN;
  551.                 saw_paren = True;
  552.             } else
  553.                 state = ME_FUNCTIONx;
  554.         } else if (isalnum(c) || c == '_' || c == '-') {
  555.             if (nx < 64)
  556.                 aname[nx++] = c;
  557.         } else {
  558.             fail(2);
  559.         }
  560.         break;
  561.         case ME_FUNCTIONx:    /* space after function name */
  562.         if (isspace(c))
  563.             continue;
  564.         else if (c == '(') {
  565.             nx = 0;
  566.             state = ME_LPAREN;
  567.         } else if (c == '"') {
  568.             nx = 0;
  569.             state = ME_S_QPARM;
  570.         }
  571.         else { state = ME_S_PARM;
  572.             nx = 0;
  573.             parm[nx++] = c;
  574.         }
  575.         break;
  576.         case ME_LPAREN:
  577.         if (isspace(c))
  578.             continue;
  579.         else if (c == '"')
  580.             state = ME_P_QPARM;
  581.         else if (c == ',') {
  582.             parm[nx++] = '\0';
  583.             params[++count] = &parm[nx];
  584.         } else if (c == ')')
  585.             goto success;
  586.         else {
  587.             state = ME_P_PARM;
  588.             parm[nx++] = c;
  589.         }
  590.         break;
  591.         case ME_P_PARM:
  592.         if (isspace(c)) {
  593.             parm[nx++] = '\0';
  594.             params[++count] = &parm[nx];
  595.             state = ME_P_PARMx;
  596.         } else if (c == ')') {
  597.             parm[nx] = '\0';
  598.             ++count;
  599.             goto success;
  600.         } else if (c == ',') {
  601.             parm[nx++] = '\0';
  602.             params[++count] = &parm[nx];
  603.             state = ME_LPAREN;
  604.         } else {
  605.             if (nx < 1024)
  606.                 parm[nx++] = c;
  607.         }
  608.         break;
  609.         case ME_P_BSL:
  610.         if (c == 'n' && nx < 1024)
  611.             parm[nx++] = '\n';
  612.         else {
  613.             if (c != '"' && nx < 1024)
  614.                 parm[nx++] = '\\';
  615.             if (nx < 1024)
  616.                 parm[nx++] = c;
  617.         }
  618.         state = ME_P_QPARM;
  619.         break;
  620.         case ME_P_QPARM:
  621.         if (c == '"') {
  622.             parm[nx++] = '\0';
  623.             params[++count] = &parm[nx];
  624.             state = ME_P_PARMx;
  625.         } else if (c == '\\') {
  626.             state = ME_P_BSL;
  627.         } else if (nx < 1024)
  628.             parm[nx++] = c;
  629.         break;
  630.         case ME_P_PARMx:
  631.         if (isspace(c))
  632.             continue;
  633.         else if (c == ',')
  634.             state = ME_LPAREN;
  635.         else if (c == ')')
  636.             goto success;
  637.         else
  638.             fail(3);
  639.         break;
  640.         case ME_S_PARM:
  641.         if (isspace(c)) {
  642.             parm[nx++] = '\0';
  643.             params[++count] = &parm[nx];
  644.             state = ME_S_PARMx;
  645.         } else {
  646.             if (nx < 1024)
  647.                 parm[nx++] = c;
  648.         }
  649.         break;
  650.         case ME_S_BSL:
  651.         if (c == 'n' && nx < 1024)
  652.             parm[nx++] = '\n';
  653.         else {
  654.             if (c != '"' && nx < 1024)
  655.                 parm[nx++] = '\\';
  656.             if (nx < 1024)
  657.                 parm[nx++] = c;
  658.         }
  659.         state = ME_S_QPARM;
  660.         break;
  661.         case ME_S_QPARM:
  662.         if (c == '"') {
  663.             parm[nx++] = '\0';
  664.             params[++count] = &parm[nx];
  665.             state = ME_S_PARMx;
  666.         } else if (c == '\\') {
  667.             state = ME_S_BSL;
  668.         } else if (nx < 1024)
  669.             parm[nx++] = c;
  670.         break;
  671.         case ME_S_PARMx:
  672.         if (isspace(c))
  673.             continue;
  674.         else if (c == '"')
  675.             state = ME_S_QPARM;
  676.         else {
  677.             parm[nx++] = c;
  678.             state = ME_S_PARM;
  679.         }
  680.         break;
  681.     }
  682.  
  683.     /* Terminal state. */
  684.     switch (state) {
  685.         case ME_FUNCTION:    /* mid-function-name */
  686.         aname[nx] = '\0';
  687.         break;
  688.         case ME_FUNCTIONx:    /* space after function */
  689.         break;
  690.         case ME_GND:    /* nothing */
  691.         if (np)
  692.             *np = s - 1;
  693.         return EM_CONTINUE;
  694.         case ME_S_PARMx:    /* space after space-style parameter */
  695.         break;
  696.         case ME_S_PARM:    /* mid space-style parameter */
  697.         parm[nx++] = '\0';
  698.         params[++count] = &parm[nx];
  699.         break;
  700.         default:
  701.         fail(3);
  702.     }
  703.  
  704.     success:
  705.     if (c) {
  706.         while (*s && isspace(*s))
  707.             s++;
  708.         if (*s) {
  709.             if (np)
  710.                 *np = s;
  711.             else
  712.                 fail(4);
  713.         } else if (np)
  714.             *np = s;
  715.     } else if (np)
  716.         *np = s-1;
  717.  
  718.     /* If it's a macro, do variable substitutions. */
  719.     if (cause == IA_MACRO || cause == IA_KEYMAP || cause == IA_COMMAND) {
  720.         Cardinal j;
  721.  
  722.         for (j = 0; j < count; j++)
  723.             params[j] = do_subst(params[j], True, False);
  724.     }
  725.  
  726.     /* Search the action list. */
  727.     if (!strncasecmp(aname, PA_PFX, strlen(PA_PFX))) {
  728.         popup_an_error("Invalid action: %s", aname);
  729.         free_params();
  730.         return EM_ERROR;
  731.     }
  732.     any = -1;
  733.     exact = -1;
  734.     for (i = 0; i < actioncount; i++) {
  735.         if (!strcasecmp(aname, actions[i].string)) {
  736.             exact = any = i;
  737.             break;
  738.         }
  739.     }
  740.     if (exact < 0) {
  741.         for (i = 0; i < actioncount; i++) {
  742.             if (!strncasecmp(aname, actions[i].string,
  743.                 strlen(aname))) {
  744.                 if (any >= 0) {
  745.                     popup_an_error("Ambiguous action name: "
  746.                         "%s", aname);
  747.                     free_params();
  748.                     return EM_ERROR;
  749.                 }
  750.                 any = i;
  751.             }
  752.         }
  753.     }
  754.     if (any >= 0) {
  755.         ia_cause = cause;
  756.         (*actions[any].proc)((Widget)NULL, (XEvent *)NULL,
  757.             count? params: (String *)NULL, &count);
  758.         free_params();
  759.         screen_disp();
  760.     } else {
  761.         popup_an_error("Unknown action: %s", aname);
  762.         free_params();
  763.         return EM_ERROR;
  764.     }
  765.  
  766. #if defined(X3270_FT) /*[*/
  767.     if (ft_state != FT_NONE)
  768.         sms->state = SS_FT_WAIT;
  769. #endif /*]*/
  770.     if (KBWAIT)
  771.         return EM_PAUSE;
  772.     else
  773.         return EM_CONTINUE;
  774.  
  775.     failure:
  776.     popup_an_error(fail_text[failreason-1]);
  777.     return EM_ERROR;
  778. #undef fail
  779. #undef free_params
  780. }
  781.  
  782. /* Run the string at the top of the stack. */
  783. static void
  784. run_string(void)
  785. {
  786.     int len;
  787.     int len_left;
  788.  
  789. #if defined(X3270_TRACE) /*[*/
  790.     if (toggled(DS_TRACE))
  791.         (void) fprintf(tracef, "%s[%d] running\n",
  792.             ST_NAME, sms_depth);
  793. #endif /*]*/
  794.  
  795.     sms->state = SS_RUNNING;
  796.     len = strlen(sms->dptr);
  797. #if defined(X3270_TRACE) /*[*/
  798.     if (toggled(DS_TRACE))
  799.         (void) fprintf(tracef, "%sString[%d]: '%s'\n",
  800.             sms->is_hex ? "Hex" : "",
  801.             sms_depth, sms->dptr);
  802. #endif /*]*/
  803.  
  804.     if (sms->is_hex) {
  805.         if (KBWAIT) {
  806.             sms->state = SS_KBWAIT;
  807. #if defined(X3270_TRACE) /*[*/
  808.             if (toggled(DS_TRACE))
  809.                 (void) fprintf(tracef,
  810.                     "%s[%d] paused %s\n",
  811.                     ST_NAME, sms_depth,
  812.                     sms_state_name[sms->state]);
  813. #endif /*]*/
  814.         } else {
  815.             hex_input(sms->dptr);
  816.             sms_pop(False);
  817.         }
  818.     } else {
  819.         if ((len_left = emulate_input(sms->dptr, len, False))) {
  820.             sms->dptr += len - len_left;
  821.             if (KBWAIT) {
  822.                 sms->state = SS_KBWAIT;
  823. #if defined(X3270_TRACE) /*[*/
  824.                 if (toggled(DS_TRACE))
  825.                     (void) fprintf(tracef,
  826.                         "%s[%d] paused %s\n",
  827.                         ST_NAME, sms_depth,
  828.                         sms_state_name[sms->state]);
  829. #endif /*]*/
  830.             }
  831.         } else {
  832.             sms_pop(False);
  833.         }
  834.     }
  835. }
  836.  
  837. /* Run the macro at the top of the stack. */
  838.  
  839. static void
  840. run_macro(void)
  841. {
  842.     char *a = sms->dptr;
  843.     char *nextm;
  844.     enum em_stat es;
  845.     sms_t *s;
  846.  
  847. #if defined(X3270_TRACE) /*[*/
  848.     if (toggled(DS_TRACE))
  849.         (void) fprintf(tracef, "%s[%d] running\n",
  850.             ST_NAME, sms_depth);
  851. #endif /*]*/
  852.  
  853.     /*
  854.      * Keep executing commands off the line until one pauses or
  855.      * we run out of commands.
  856.      */
  857.     while (*a) {
  858.         /*
  859.          * Check for command failure.
  860.          */
  861.         if (!sms->success) {
  862. #if defined(X3270_TRACE) /*[*/
  863.             if (toggled(DS_TRACE))
  864.                 (void) fprintf(tracef, "%s[%d] failed\n",
  865.                     ST_NAME, sms_depth);
  866. #endif /*]*/
  867.             /* Propogate it. */
  868.             if (sms->next != SN)
  869.                 sms->next->success = False;
  870.             break;
  871.         }
  872.  
  873.         sms->state = SS_RUNNING;
  874. #if defined(X3270_TRACE) /*[*/
  875.         if (toggled(DS_TRACE))
  876.             (void) fprintf(tracef, "%s[%d]: '%s'\n",
  877.                 ST_NAME, sms_depth, a);
  878. #endif /*]*/
  879.         s = sms;
  880.         s->success = True;
  881.         s->executing = True;
  882.         es = execute_command((s->type == ST_MACRO)? IA_MACRO:
  883.             ((s->type == ST_COMMAND)? IA_COMMAND: IA_KEYMAP),
  884.             a, &nextm);
  885.         s->executing = False;
  886.         s->dptr = nextm;
  887.  
  888.         /*
  889.          * If a new sms was started, we will be resumed
  890.          * when it completes.
  891.          */
  892.         if (sms != s) {
  893.             return;
  894.         }
  895.  
  896.         /* Macro could not execute.  Abort it. */
  897.         if (es == EM_ERROR) {
  898. #if defined(X3270_TRACE) /*[*/
  899.             if (toggled(DS_TRACE))
  900.                 (void) fprintf(tracef, "%s[%d] error\n",
  901.                     ST_NAME, sms_depth);
  902. #endif /*]*/
  903.             /* Propogate it. */
  904.             if (sms->next != SN)
  905.                 sms->next->success = False;
  906.             break;
  907.         }
  908.  
  909.         /* Macro paused, implicitly or explicitly.  Suspend it. */
  910.         if (es == EM_PAUSE || (int)sms->state >= (int)SS_KBWAIT) {
  911.             if (sms->state == SS_RUNNING)
  912.                 sms->state = SS_KBWAIT;
  913. #if defined(X3270_TRACE) /*[*/
  914.             if (toggled(DS_TRACE))
  915.                 (void) fprintf(tracef, "%s[%d] paused %s\n",
  916.                     ST_NAME, sms_depth,
  917.                     sms_state_name[sms->state]);
  918. #endif /*]*/
  919.             sms->dptr = nextm;
  920.             return;
  921.         }
  922.  
  923.         /* Macro ran. */
  924.         a = nextm;
  925.     }
  926.  
  927.     /* Finished with this macro. */
  928.     sms_pop(False);
  929. }
  930.  
  931. /* Push a macro (macro, command or keymap action) on the stack. */
  932. static void
  933. push_xmacro(enum sms_type type, char *s, Boolean is_login)
  934. {
  935.     macro_output = False;
  936.     if (!sms_push(type))
  937.         return;
  938.     (void) strncpy(sms->msc, s, 1023);
  939.     sms->msc[1023] = '\0';
  940.     sms->msc_len = strlen(s);
  941.     if (sms->msc_len > 1023)
  942.         sms->msc_len = 1023;
  943.     if (is_login) {
  944.         sms->state = SS_WAIT;
  945.         sms->is_login = True;
  946.     } else
  947.         sms->state = SS_INCOMPLETE;
  948.     if (sms_depth == 1)
  949.         sms_continue();
  950. }
  951.  
  952. /* Push a macro on the stack. */
  953. void
  954. push_macro(char *s, Boolean is_login)
  955. {
  956.     push_xmacro(ST_MACRO, s, is_login);
  957. }
  958.  
  959. /* Push an interactive command on the stack. */
  960. void
  961. push_command(char *s)
  962. {
  963.     push_xmacro(ST_COMMAND, s, False);
  964. }
  965.  
  966. /* Push an keymap action on the stack. */
  967. void
  968. push_keymap_action(char *s)
  969. {
  970.     push_xmacro(ST_KEYMAP, s, False);
  971. }
  972.  
  973. /* Push a string on the stack. */
  974. static void
  975. push_string(char *s, Boolean is_login, Boolean is_hex)
  976. {
  977.     if (!sms_push(ST_STRING))
  978.         return;
  979.     (void) strncpy(sms->msc, s, 1023);
  980.     sms->msc[1023] = '\0';
  981.     sms->msc_len = strlen(s);
  982.     if (sms->msc_len > 1023)
  983.         sms->msc_len = 1023;
  984.     if (is_login) {
  985.         sms->state = SS_WAIT;
  986.         sms->is_login = True;
  987.     } else
  988.         sms->state = SS_INCOMPLETE;
  989.     sms->is_hex = is_hex;
  990.     if (sms_depth == 1)
  991.         sms_continue();
  992. }
  993.  
  994. /* Set a pending string. */
  995. void
  996. ps_set(char *s, Boolean is_hex)
  997. {
  998.     push_string(s, False, is_hex);
  999. }
  1000.  
  1001. #if defined(X3270_MENUS) /*[*/
  1002. /* Callback for macros menu. */
  1003. void
  1004. macro_command(struct macro_def *m)
  1005. {
  1006.     push_macro(m->action, False);
  1007. }
  1008. #endif /*]*/
  1009.  
  1010. /*
  1011.  * If the string looks like an action, e.g., starts with "Xxx(", run a login
  1012.  * macro.  Otherwise, set a simple pending login string.
  1013.  */
  1014. void
  1015. login_macro(char *s)
  1016. {
  1017.     char *t = s;
  1018.     Boolean looks_right = False;
  1019.  
  1020.     while (isspace(*t))
  1021.         t++;
  1022.     if (isalnum(*t)) {
  1023.         while (isalnum(*t))
  1024.             t++;
  1025.         while (isspace(*t))
  1026.             t++;
  1027.         if (*t == '(')
  1028.             looks_right = True;
  1029.     }
  1030.  
  1031.     if (looks_right)
  1032.         push_macro(s, True);
  1033.     else
  1034.         push_string(s, True, False);
  1035. }
  1036.  
  1037. /* Run the first command in the msc[] buffer. */
  1038. static void
  1039. run_script(void)
  1040. {
  1041. #if defined(X3270_TRACE) /*[*/
  1042.     if (toggled(DS_TRACE))
  1043.         (void) fprintf(tracef, "%s[%d] running\n",
  1044.             ST_NAME, sms_depth);
  1045. #endif /*]*/
  1046.  
  1047.     for (;;) {
  1048.         char *ptr;
  1049.         int cmd_len;
  1050.         char *cmd;
  1051.         sms_t *s;
  1052.         enum em_stat es;
  1053.  
  1054.         /* If the script isn't idle, we're done. */
  1055.         if (sms->state != SS_IDLE)
  1056.             break;
  1057.  
  1058.         /* If a prompt is required, send one. */
  1059.         if (sms->need_prompt) {
  1060.             script_prompt(sms->success);
  1061.             sms->need_prompt = False;
  1062.         }
  1063.  
  1064.         /* If there isn't a pending command, we're done. */
  1065.         if (!sms->msc_len)
  1066.             break;
  1067.  
  1068.         /* Isolate the command. */
  1069.         ptr = memchr(sms->msc, '\n', sms->msc_len);
  1070.         if (!ptr)
  1071.             break;
  1072.         *ptr++ = '\0';
  1073.         cmd_len = ptr - sms->msc;
  1074.         cmd = sms->msc;
  1075.  
  1076.         /* Execute it. */
  1077.         sms->state = SS_RUNNING;
  1078.         sms->success = True;
  1079. #if defined(X3270_TRACE) /*[*/
  1080.         if (toggled(DS_TRACE))
  1081.             (void) fprintf(tracef, "%s[%d]: '%s'\n", ST_NAME,
  1082.                 sms_depth, cmd);
  1083. #endif /*]*/
  1084.         s = sms;
  1085.         s->executing = True;
  1086.         es = execute_command(IA_SCRIPT, cmd, (char **)NULL);
  1087.         s->executing = False;
  1088.  
  1089.         /* Move the rest of the buffer over. */
  1090.         if (cmd_len < s->msc_len) {
  1091.             s->msc_len -= cmd_len;
  1092.             (void) memmove(s->msc, ptr, s->msc_len);
  1093.         } else
  1094.             s->msc_len = 0;
  1095.  
  1096.         /*
  1097.          * If a new sms was started, we will be resumed
  1098.          * when it completes.
  1099.          */
  1100.         if (sms != s) {
  1101.             s->need_prompt = True;
  1102.             return;
  1103.         }
  1104.  
  1105.         /* Handle what it did. */
  1106.         if (es == EM_PAUSE || (int)sms->state >= (int)SS_KBWAIT) {
  1107.             if (sms->state == SS_RUNNING)
  1108.                 sms->state = SS_KBWAIT;
  1109.             script_disable();
  1110.             if (sms->state == SS_CLOSING) {
  1111.                 sms_pop(False);
  1112.                 return;
  1113.             }
  1114.             sms->need_prompt = True;
  1115.         } else if (es == EM_ERROR) {
  1116. #if defined(X3270_TRACE) /*[*/
  1117.             if (toggled(DS_TRACE))
  1118.                 (void) fprintf(tracef, "%s[%d] error\n",
  1119.                     ST_NAME, sms_depth);
  1120. #endif /*]*/
  1121.             script_prompt(False);
  1122.         } else
  1123.             script_prompt(sms->success);
  1124.         if (sms->state == SS_RUNNING)
  1125.             sms->state = SS_IDLE;
  1126.         else {
  1127. #if defined(X3270_TRACE) /*[*/
  1128.             if (toggled(DS_TRACE))
  1129.                 (void) fprintf(tracef, "%s[%d] paused %s\n",
  1130.                     ST_NAME, sms_depth,
  1131.                     sms_state_name[sms->state]);
  1132. #endif /*]*/
  1133.         }
  1134.     }
  1135. }
  1136.  
  1137. /* Handle an error generated during the execution of a script or macro. */
  1138. void
  1139. sms_error(char *msg)
  1140. {
  1141.     /* Print the error message. */
  1142.     (void) fprintf(stderr, "%s\n", msg);
  1143.  
  1144.     /* Fail the command. */
  1145.     sms->success = False;
  1146.  
  1147.     /* Cancel any login. */
  1148.     if (sms->is_login)
  1149.         host_disconnect(True);
  1150. }
  1151.  
  1152. /* Generate a response to a script command. */
  1153. void
  1154. sms_info(const char *fmt, ...)
  1155. {
  1156.     char *nl;
  1157.     char msgbuf[4096];
  1158.     char *msg = msgbuf;
  1159.     va_list args;
  1160.  
  1161.     va_start(args, fmt);
  1162.     vsprintf(msgbuf, fmt, args);
  1163.     va_end(args);
  1164.  
  1165.     do {
  1166.         int nc;
  1167.  
  1168.         nl = strchr(msg, '\n');
  1169.         if (nl != CN) {
  1170.             nc = nl - msg;
  1171.         } else
  1172.             nc = strlen(msg);
  1173.         if (nc) {
  1174.             switch (sms->type) {
  1175.             case ST_PEER:
  1176.             case ST_CHILD:
  1177.                 (void) fprintf(sms->outfile, "data: %.*s\n",
  1178.                     nc, msg);
  1179.                 break;
  1180.             default:
  1181.                 (void) printf("%.*s\n", nc, msg);
  1182.                 break;
  1183.             }
  1184.         }
  1185.         msg = nl + 1;
  1186.     } while (nl);
  1187.  
  1188.     macro_output = True;
  1189. }
  1190.  
  1191. /* Process available input from a script. */
  1192. static void
  1193. script_input(void)
  1194. {
  1195.     char buf[128];
  1196.     int nr;
  1197.     char *ptr;
  1198.     char c;
  1199.  
  1200. #if defined(X3270_TRACE) /*[*/
  1201.     if (toggled(DS_TRACE))
  1202.         (void) fprintf(tracef, "Input for %s[%d] %d\n",
  1203.             ST_NAME, sms_depth, sms->state);
  1204. #endif /*]*/
  1205.  
  1206.     /* Read in what you can. */
  1207.     nr = read(sms->infd, buf, sizeof(buf));
  1208.     if (nr < 0) {
  1209.         popup_an_errno(errno, "%s[%d] read", ST_NAME, sms_depth);
  1210.         return;
  1211.     }
  1212.     if (nr == 0) {    /* end of file */
  1213. #if defined(X3270_TRACE) /*[*/
  1214.         if (toggled(DS_TRACE))
  1215.             (void) fprintf(tracef, "EOF %s[%d]\n",
  1216.                 ST_NAME, sms_depth);
  1217. #endif /*]*/
  1218.         sms_pop(True);
  1219.         sms_continue();
  1220.         return;
  1221.     }
  1222.  
  1223.     /* Append to the pending command, ignoring returns. */
  1224.     ptr = buf;
  1225.     while (nr--)
  1226.         if ((c = *ptr++) != '\r')
  1227.             sms->msc[sms->msc_len++] = c;
  1228.  
  1229.     /* Run the command(s). */
  1230.     sms->state = SS_INCOMPLETE;
  1231.     sms_continue();
  1232. }
  1233.  
  1234. /* Resume a paused sms, if conditions are now ripe. */
  1235. void
  1236. sms_continue(void)
  1237. {
  1238.     while (True) {
  1239.         if (sms == SN)
  1240.             return;
  1241.  
  1242.         switch (sms->state) {
  1243.  
  1244.             case SS_IDLE:
  1245.             return;        /* nothing to do */
  1246.  
  1247.             case SS_INCOMPLETE:
  1248.             case SS_RUNNING:
  1249.             break;        /* let it proceed */
  1250.  
  1251.             case SS_KBWAIT:
  1252.             if (KBWAIT)
  1253.                 return;
  1254.             break;
  1255.  
  1256.             case SS_WAIT_ANSI:
  1257.             if (IN_ANSI) {
  1258.                 sms->state = SS_WAIT;
  1259.                 continue;
  1260.             }
  1261.             return;
  1262.  
  1263.             case SS_WAIT_3270:
  1264.             if (IN_3270 | IN_SSCP) {
  1265.                 sms->state = SS_WAIT;
  1266.                 continue;
  1267.             }
  1268.             return;
  1269.  
  1270.             case SS_WAIT:
  1271.             if (!CAN_PROCEED)
  1272.                 return;
  1273.             /* fall through... */
  1274.             case SS_CONNECT_WAIT:
  1275.             if (HALF_CONNECTED ||
  1276.                 (CONNECTED && (kybdlock & KL_AWAITING_FIRST)))
  1277.                 return;
  1278.             if (!CONNECTED) {
  1279.                 /* connection failed */
  1280.                 if (sms->need_prompt) {
  1281.                     script_prompt(False);
  1282.                     sms->need_prompt = False;
  1283.                 }
  1284.                 break;
  1285.             }
  1286.             break;
  1287.  
  1288. #if defined(X3270_FT) /*[*/
  1289.             case SS_FT_WAIT:
  1290.             if (ft_state == FT_NONE)
  1291.                 break;
  1292.             else
  1293.                 return;
  1294. #endif /*]*/
  1295.  
  1296.             case SS_WAIT_OUTPUT:
  1297.             case SS_SWAIT_OUTPUT:
  1298.             return;
  1299.  
  1300.             case SS_WAIT_DISC:
  1301.             if (!CONNECTED)
  1302.                 break;
  1303.             else
  1304.                 return;
  1305.  
  1306.             case SS_PAUSED:
  1307.             return;
  1308.  
  1309.             case SS_EXPECTING:
  1310.             return;
  1311.  
  1312.             case SS_CLOSING:
  1313.             return;    /* can't happen, I hope */
  1314.  
  1315.         }
  1316.  
  1317.         /* Restart the sms. */
  1318.  
  1319.         sms->state = SS_IDLE;
  1320.  
  1321.         if (sms->wait_id != 0L) {
  1322.             RemoveTimeOut(sms->wait_id);
  1323.             sms->wait_id = 0L;
  1324.         }
  1325.  
  1326.         switch (sms->type) {
  1327.             case ST_STRING:
  1328.             run_string();
  1329.             break;
  1330.             case ST_MACRO:
  1331.             case ST_COMMAND:
  1332.             case ST_KEYMAP:
  1333.             run_macro();
  1334.             break;
  1335.             case ST_PEER:
  1336.             case ST_CHILD:
  1337.             script_enable();
  1338.             run_script();
  1339.             break;
  1340.         }
  1341.     }
  1342. }
  1343.  
  1344. /*
  1345.  * Macro- and script-specific actions.
  1346.  */
  1347.  
  1348. static void
  1349. dump_range(int first, int len, Boolean in_ascii, unsigned char *buf,
  1350.     int rel_rows unused, int rel_cols)
  1351. {
  1352.     register int i;
  1353.     Boolean any = False;
  1354.     char *linebuf;
  1355.     char *s;
  1356.  
  1357.     linebuf = Malloc(maxCOLS * 3 + 1);
  1358.     s = linebuf;
  1359.  
  1360.     /*
  1361.      * If the client has looked at the live screen, then if they later
  1362.      * execute 'Wait(output)', they will need to wait for output from the
  1363.      * host.  output_wait_needed is cleared by sms_host_output,
  1364.      * which is called from the write logic in ctlr.c.
  1365.      */     
  1366.     if (sms != SN && buf == screen_buf)
  1367.         sms->output_wait_needed = True;
  1368.  
  1369.     for (i = 0; i < len; i++) {
  1370.         unsigned char c;
  1371.  
  1372.         if (i && !((first + i) % rel_cols)) {
  1373.             *s = '\0';
  1374.             action_output(linebuf);
  1375.             s = linebuf;
  1376.             any = False;
  1377.         }
  1378.         if (!any)
  1379.             any = True;
  1380.         if (in_ascii) {
  1381.             c = cg2asc[buf[first + i]];
  1382.             s += sprintf(s, "%c", c ? c : ' ');
  1383.         } else {
  1384.             s += sprintf(s, "%s%02x",
  1385.                 i ? " " : "",
  1386.                 cg2ebc[buf[first + i]]);
  1387.         }
  1388.     }
  1389.     if (any) {
  1390.         *s = '\0';
  1391.         action_output(linebuf);
  1392.     }
  1393.     Free(linebuf);
  1394. }
  1395.  
  1396. static void
  1397. dump_fixed(String params[], Cardinal count, const char *name, Boolean in_ascii,
  1398.     unsigned char *buf, int rel_rows, int rel_cols, int caddr)
  1399. {
  1400.     int row, col, len, rows = 0, cols = 0;
  1401.  
  1402.     switch (count) {
  1403.         case 0:    /* everything */
  1404.         row = 0;
  1405.         col = 0;
  1406.         len = rel_rows*rel_cols;
  1407.         break;
  1408.         case 1:    /* from cursor, for n */
  1409.         row = caddr / rel_cols;
  1410.         col = caddr % rel_cols;
  1411.         len = atoi(params[0]);
  1412.         break;
  1413.         case 3:    /* from (row,col), for n */
  1414.         row = atoi(params[0]);
  1415.         col = atoi(params[1]);
  1416.         len = atoi(params[2]);
  1417.         break;
  1418.         case 4:    /* from (row,col), for rows x cols */
  1419.         row = atoi(params[0]);
  1420.         col = atoi(params[1]);
  1421.         rows = atoi(params[2]);
  1422.         cols = atoi(params[3]);
  1423.         len = 0;
  1424.         break;
  1425.         default:
  1426.         popup_an_error("%s requires 0, 1, 3 or 4 arguments", name);
  1427.         return;
  1428.     }
  1429.  
  1430.     if (
  1431.         (row < 0 || row > rel_rows || col < 0 || col > rel_cols || len < 0) ||
  1432.         ((count < 4)  && ((row * rel_cols) + col + len > rel_rows * rel_cols)) ||
  1433.         ((count == 4) && (cols < 0 || rows < 0 ||
  1434.                   col + cols > rel_cols || row + rows > rel_rows))
  1435.        ) {
  1436.         popup_an_error("%s: Invalid argument", name);
  1437.         return;
  1438.     }
  1439.     if (count < 4)
  1440.         dump_range((row * rel_cols) + col, len, in_ascii, buf,
  1441.             rel_rows, rel_cols);
  1442.     else {
  1443.         int i;
  1444.  
  1445.         for (i = 0; i < rows; i++)
  1446.             dump_range(((row+i) * rel_cols) + col, cols, in_ascii,
  1447.                 buf, rel_rows, rel_cols);
  1448.     }
  1449. }
  1450.  
  1451. static void
  1452. dump_field(Cardinal count, const char *name, Boolean in_ascii)
  1453. {
  1454.     unsigned char *fa;
  1455.     int start, baddr;
  1456.     int len = 0;
  1457.  
  1458.     if (count != 0) {
  1459.         popup_an_error("%s requires 0 arguments", name);
  1460.         return;
  1461.     }
  1462.     if (!formatted) {
  1463.         popup_an_error("%s: Screen is not formatted", name);
  1464.         return;
  1465.     }
  1466.     fa = get_field_attribute(cursor_addr);
  1467.     start = fa - screen_buf;
  1468.     INC_BA(start);
  1469.     baddr = start;
  1470.     do {
  1471.         if (IS_FA(screen_buf[baddr]))
  1472.             break;
  1473.         len++;
  1474.         INC_BA(baddr);
  1475.     } while (baddr != start);
  1476.     dump_range(start, len, in_ascii, screen_buf, ROWS, COLS);
  1477. }
  1478.  
  1479. void
  1480. Ascii_action(Widget w unused, XEvent *event unused, String *params,
  1481.     Cardinal *num_params)
  1482. {
  1483.     dump_fixed(params, *num_params, action_name(Ascii_action), True,
  1484.         screen_buf, ROWS, COLS, cursor_addr);
  1485. }
  1486.  
  1487. void
  1488. AsciiField_action(Widget w unused, XEvent *event unused, String *params unused,
  1489.     Cardinal *num_params)
  1490. {
  1491.     dump_field(*num_params, action_name(AsciiField_action), True);
  1492. }
  1493.  
  1494. void
  1495. Ebcdic_action(Widget w unused, XEvent *event unused, String *params,
  1496.     Cardinal *num_params)
  1497. {
  1498.     dump_fixed(params, *num_params, action_name(Ebcdic_action), False,
  1499.         screen_buf, ROWS, COLS, cursor_addr);
  1500. }
  1501.  
  1502. void
  1503. EbcdicField_action(Widget w unused, XEvent *event unused,
  1504.     String *params unused, Cardinal *num_params)
  1505. {
  1506.     dump_field(*num_params, action_name(EbcdicField_action), False);
  1507. }
  1508.  
  1509. /*
  1510.  * The sms prompt is preceeded by a status line with 11 fields:
  1511.  *
  1512.  *  1 keyboard status
  1513.  *     U unlocked
  1514.  *     L locked, waiting for host response
  1515.  *     E locked, keying error
  1516.  *  2 formatting status of screen
  1517.  *     F formatted
  1518.  *     U unformatted
  1519.  *  3 protection status of current field
  1520.  *     U unprotected (modifiable)
  1521.  *     P protected
  1522.  *  4 connect status
  1523.  *     N not connected
  1524.  *     C(host) connected
  1525.  *  5 emulator mode
  1526.  *     N not connected
  1527.  *     C connected in ANSI character mode
  1528.  *     L connected in ANSI line mode
  1529.  *     P 3270 negotiation pending
  1530.  *     I connected in 3270 mode
  1531.  *  6 model number
  1532.  *  7 rows
  1533.  *  8 cols
  1534.  *  9 cursor row
  1535.  * 10 cursor col
  1536.  * 11 main window id
  1537.  */
  1538. static char *
  1539. status_string(void)
  1540. {
  1541.     char kb_stat;
  1542.     char fmt_stat;
  1543.     char prot_stat;
  1544.     char *connect_stat = CN;
  1545.     char em_mode;
  1546.     char s[1024];
  1547.     char *r;
  1548.  
  1549.     if (!kybdlock)
  1550.         kb_stat = 'U';
  1551.     else if (!CONNECTED || KBWAIT)
  1552.         kb_stat = 'L';
  1553.     else
  1554.         kb_stat = 'E';
  1555.  
  1556.     if (formatted)
  1557.         fmt_stat = 'F';
  1558.     else
  1559.         fmt_stat = 'U';
  1560.  
  1561.     if (!formatted)
  1562.         prot_stat = 'U';
  1563.     else {
  1564.         unsigned char *fa;
  1565.  
  1566.         fa = get_field_attribute(cursor_addr);
  1567.         if (FA_IS_PROTECTED(*fa))
  1568.             prot_stat = 'P';
  1569.         else
  1570.             prot_stat = 'U';
  1571.     }
  1572.  
  1573.     if (CONNECTED)
  1574.         connect_stat = xs_buffer("C(%s)", current_host);
  1575.     else
  1576.         connect_stat = NewString("N");
  1577.  
  1578.     if (CONNECTED) {
  1579.         if (IN_ANSI) {
  1580.             if (linemode)
  1581.                 em_mode = 'L';
  1582.             else
  1583.                 em_mode = 'C';
  1584.         } else if (IN_3270)
  1585.             em_mode = 'I';
  1586.         else
  1587.             em_mode = 'P';
  1588.     } else
  1589.         em_mode = 'N';
  1590.  
  1591.     (void) sprintf(s,
  1592.         "%c %c %c %s %c %d %d %d %d %d 0x%lx",
  1593.         kb_stat,
  1594.         fmt_stat,
  1595.         prot_stat,
  1596.         connect_stat,
  1597.         em_mode,
  1598.         model_num,
  1599.         ROWS, COLS,
  1600.         cursor_addr / COLS, cursor_addr % COLS,
  1601. #if defined(X3270_DISPLAY) /*[*/
  1602.         XtWindow(toplevel)
  1603. #else /*][*/
  1604.         0L
  1605. #endif /*]*/
  1606.         );
  1607.  
  1608.     r = NewString(s);
  1609.     Free(connect_stat);
  1610.     return r;
  1611. }
  1612.  
  1613. static void
  1614. script_prompt(Boolean success)
  1615. {
  1616.     char *s;
  1617.  
  1618.     s = status_string();
  1619.     (void) fprintf(sms->outfile, "%s\n%s\n", s, success ? "ok" : "error");
  1620.     (void) fflush(sms->outfile);
  1621.     Free(s);
  1622. }
  1623.  
  1624. /* Save the state of the screen for Snap queries. */
  1625. static char *snap_status = NULL;
  1626. static unsigned char *snap_buf = NULL;
  1627. static int snap_rows = 0;
  1628. static int snap_cols = 0;
  1629. static int snap_field_start = -1;
  1630. static int snap_field_length = -1;
  1631. static int snap_caddr = 0;
  1632.  
  1633. static void
  1634. snap_save(void)
  1635. {
  1636.     sms->output_wait_needed = True;
  1637.     if (snap_status != NULL)
  1638.         Free(snap_status);
  1639.     snap_status = status_string();
  1640.  
  1641.     if (snap_buf != NULL)
  1642.         Free(snap_buf);
  1643.     snap_buf = (unsigned char *)Malloc(ROWS*COLS);
  1644.     (void) memcpy(snap_buf, screen_buf, ROWS*COLS);
  1645.  
  1646.     snap_rows = ROWS;
  1647.     snap_cols = COLS;
  1648.  
  1649.     if (!formatted) {
  1650.         snap_field_start = -1;
  1651.         snap_field_length = -1;
  1652.     } else {
  1653.         unsigned char *fa;
  1654.         int baddr;
  1655.  
  1656.         snap_field_length = 0;
  1657.         fa = get_field_attribute(cursor_addr);
  1658.         snap_field_start = fa - screen_buf;
  1659.         INC_BA(snap_field_start);
  1660.         baddr = snap_field_start;
  1661.         do {
  1662.             if (IS_FA(screen_buf[baddr]))
  1663.                 break;
  1664.             snap_field_length++;
  1665.             INC_BA(baddr);
  1666.         } while (baddr != snap_field_start);
  1667.     }
  1668.     snap_caddr = cursor_addr;
  1669. }
  1670.  
  1671. /*
  1672.  * "Snap" action, maintains a snapshot for consistent multi-field comparisons:
  1673.  *
  1674.  *  Snap [Save]
  1675.  *    updates the saved image from the live image
  1676.  *  Snap Rows
  1677.  *    returns the number of rows
  1678.  *  Snap Cols
  1679.  *    returns the number of columns
  1680.  *  Snap Staus
  1681.  *  Snap Ascii ...
  1682.  *  Snap AsciiField (not yet)
  1683.  *  Snap Ebcdic ...
  1684.  *  Snap EbcdicField (not yet)
  1685.  *    runs the named command
  1686.  *  Snap Wait [tmo] Output
  1687.  *      wait for the screen to change, then do a Snap Save
  1688.  */
  1689. void
  1690. Snap_action(Widget w unused, XEvent *event unused, String *params,
  1691.     Cardinal *num_params)
  1692. {
  1693.     if (sms == SN || sms->state != SS_RUNNING) {
  1694.         popup_an_error("%s can only be called from scripts or macros",
  1695.             action_name(Snap_action));
  1696.         return;
  1697.     }
  1698.  
  1699.     if (*num_params == 0) {
  1700.         snap_save();
  1701.         return;
  1702.     }
  1703.  
  1704.     /* Handle 'Snap Wait' separately. */
  1705.     if (!strcasecmp(params[0], action_name(Wait_action))) {
  1706.         unsigned long tmo;
  1707.         char *ptr;
  1708.         int maxp = 0;
  1709.  
  1710.         if (*num_params > 1 &&
  1711.             (tmo = strtoul(params[1], &ptr, 10)) > 0 &&
  1712.             ptr != params[0] &&
  1713.             *ptr == '\0') {
  1714.             maxp = 3;
  1715.         } else {
  1716.             tmo = 0L;
  1717.             maxp = 2;
  1718.         }
  1719.         if (*num_params > maxp) {
  1720.             popup_an_error("Too many arguments to %s %s",
  1721.                 action_name(Snap_action),
  1722.                 action_name(Wait_action));
  1723.                 return;
  1724.         }
  1725.         if (*num_params < maxp) {
  1726.             popup_an_error("Too few arguments to %s %s",
  1727.                 action_name(Snap_action),
  1728.                 action_name(Wait_action));
  1729.                 return;
  1730.         }
  1731.         if (strcasecmp(params[*num_params - 1], "Output")) {
  1732.             popup_an_error("Unknown parameter to %s %s",
  1733.                 action_name(Snap_action),
  1734.                 action_name(Wait_action));
  1735.                 return;
  1736.         }
  1737.  
  1738.         /* Must be connected. */
  1739.         if (!(CONNECTED || HALF_CONNECTED)) {
  1740.             popup_an_error("%s: Not connected",
  1741.                 action_name(Snap_action));
  1742.             return;
  1743.         }
  1744.  
  1745.         /*
  1746.          * Make sure we need to wait.
  1747.          * If we don't, then Snap(Wait) is equivalent to Snap().
  1748.          */
  1749.         if (!sms->output_wait_needed) {
  1750.             snap_save();
  1751.             return;
  1752.         }
  1753.  
  1754.         /* Set the new state. */
  1755.         sms->state = SS_SWAIT_OUTPUT;
  1756.  
  1757.         /* Set up a timeout, if they want one. */
  1758.         if (tmo)
  1759.             sms->wait_id = AddTimeOut(tmo * 1000, wait_timed_out);
  1760.         return;
  1761.     }
  1762.  
  1763.     if (!strcasecmp(params[0], "Save")) {
  1764.         if (*num_params != 1) {
  1765.             popup_an_error("Extra argument(s)");
  1766.             return;
  1767.         }
  1768.         snap_save();
  1769.     } else if (!strcasecmp(params[0], "Status")) {
  1770.         if (*num_params != 1) {
  1771.             popup_an_error("Extra argument(s)");
  1772.             return;
  1773.         }
  1774.         if (snap_status == NULL) {
  1775.             popup_an_error("No saved state");
  1776.             return;
  1777.         }
  1778.         action_output("%s", snap_status);
  1779.     } else if (!strcasecmp(params[0], "Rows")) {
  1780.         if (*num_params != 1) {
  1781.             popup_an_error("Extra argument(s)");
  1782.             return;
  1783.         }
  1784.         if (snap_status == NULL) {
  1785.             popup_an_error("No saved state");
  1786.             return;
  1787.         }
  1788.         action_output("%d", snap_rows);
  1789.     } else if (!strcasecmp(params[0], "Cols")) {
  1790.         if (*num_params != 1) {
  1791.             popup_an_error("Extra argument(s)");
  1792.             return;
  1793.         }
  1794.         if (snap_status == NULL) {
  1795.             popup_an_error("No saved state");
  1796.             return;
  1797.         }
  1798.         action_output("%d", snap_cols);
  1799.     } else if (!strcasecmp(params[0], action_name(Ascii_action))) {
  1800.         if (snap_status == NULL) {
  1801.             popup_an_error("No saved state");
  1802.             return;
  1803.         }
  1804.         dump_fixed(params + 1, *num_params - 1,
  1805.             action_name(Ascii_action), True, snap_buf,
  1806.             snap_rows, snap_cols, snap_caddr);
  1807.     } else if (!strcasecmp(params[0], action_name(Ebcdic_action))) {
  1808.         if (snap_status == NULL) {
  1809.             popup_an_error("No saved state");
  1810.             return;
  1811.         }
  1812.         dump_fixed(params + 1, *num_params - 1,
  1813.             action_name(Ebcdic_action), False, snap_buf,
  1814.             snap_rows, snap_cols, snap_caddr);
  1815.     } else {
  1816.         popup_an_error("%s: Argument must be Save, Status, Rows, Cols, "
  1817.             "%s, %s or %s",
  1818.             action_name(Snap_action),
  1819.             action_name(Wait_action),
  1820.             action_name(Ascii_action),
  1821.             action_name(Ebcdic_action));
  1822.     }
  1823. }
  1824.  
  1825. /*
  1826.  * Wait for various conditions.
  1827.  */
  1828. void
  1829. Wait_action(Widget w unused, XEvent *event unused, String *params,
  1830.     Cardinal *num_params)
  1831. {
  1832.     enum sms_state next_state = SS_WAIT;
  1833.     unsigned long tmo = 0;
  1834.     char *ptr;
  1835.     Cardinal np;
  1836.     String *pr;
  1837.  
  1838.     /* Pick off the timeout parameter first. */
  1839.     if (*num_params > 0 &&
  1840.         (tmo = strtoul(params[0], &ptr, 10)) > 0 &&
  1841.         ptr != params[0] &&
  1842.         *ptr == '\0') {
  1843.         np = *num_params - 1;
  1844.         pr = params + 1;
  1845.     } else {
  1846.         np = *num_params;
  1847.         pr = params;
  1848.     }
  1849.  
  1850.     if (check_usage(Wait_action, np, 0, 1) < 0)
  1851.         return;
  1852.     if (sms == SN || sms->state != SS_RUNNING) {
  1853.         popup_an_error("%s can only be called from scripts or macros",
  1854.             action_name(Wait_action));
  1855.         return;
  1856.     }
  1857.     if (np == 1) {
  1858.         if (!strcasecmp(pr[0], "NVTMode") ||
  1859.             !strcasecmp(pr[0], "ansi")) {
  1860.             if (!IN_ANSI)
  1861.                 next_state = SS_WAIT_ANSI;
  1862.         } else if (!strcasecmp(pr[0], "3270Mode") ||
  1863.                !strcasecmp(pr[0], "3270")) {
  1864.             if (!IN_3270)
  1865.                 next_state = SS_WAIT_3270;
  1866.         } else if (!strcasecmp(pr[0], "Output")) {
  1867.             if (sms->output_wait_needed)
  1868.                 next_state = SS_WAIT_OUTPUT;
  1869.             else
  1870.                 return;
  1871.         } else if (!strcasecmp(pr[0], "Disconnect")) {
  1872.             if (CONNECTED)
  1873.                 next_state = SS_WAIT_DISC;
  1874.             else
  1875.                 return;
  1876.         } else if (strcasecmp(pr[0], "InputField")) {
  1877.             popup_an_error("%s argument must be InputField, "
  1878.                 "NVTmode, 3270Mode, Output or Disconnect",
  1879.             action_name(Wait_action));
  1880.             return;
  1881.         }
  1882.     }
  1883.     if (!(CONNECTED || HALF_CONNECTED)) {
  1884.         popup_an_error("%s: Not connected", action_name(Wait_action));
  1885.         return;
  1886.     }
  1887.  
  1888.     /* Is it already okay? */
  1889.     if (next_state == SS_WAIT && CAN_PROCEED)
  1890.         return;
  1891.  
  1892.     /* No, wait for it to happen. */
  1893.     sms->state = next_state;
  1894.  
  1895.     /* Set up a timeout, if they want one. */
  1896.     if (tmo)
  1897.         sms->wait_id = AddTimeOut(tmo * 1000, wait_timed_out);
  1898. }
  1899.  
  1900. /*
  1901.  * Callback from Connect() and Reconnect() actions, to minimally pause a
  1902.  * running sms.
  1903.  */
  1904. void
  1905. sms_connect_wait(void)
  1906. {
  1907.     if (sms != SN &&
  1908.         (int)sms->state >= (int)SS_RUNNING &&
  1909.         sms->state != SS_WAIT) {
  1910.         if (HALF_CONNECTED ||
  1911.             (CONNECTED && (kybdlock & KL_AWAITING_FIRST)))
  1912.             sms->state = SS_CONNECT_WAIT;
  1913.     }
  1914. }
  1915.  
  1916. /*
  1917.  * Callback from ctlr.c, to indicate that the host has changed the screen.
  1918.  */
  1919. void
  1920. sms_host_output(void)
  1921. {
  1922.     if (sms != SN) {
  1923.         sms->output_wait_needed = False;
  1924.  
  1925.         switch (sms->state) {
  1926.         case SS_SWAIT_OUTPUT:
  1927.             snap_save();
  1928.             /* fall through... */
  1929.         case SS_WAIT_OUTPUT:
  1930.             sms->state = SS_RUNNING;
  1931.             sms_continue();
  1932.             break;
  1933.         default:
  1934.             break;
  1935.         }
  1936.     }
  1937. }
  1938.  
  1939. /* Return whether error pop-ups should be short-circuited. */
  1940. Boolean
  1941. sms_redirect(void)
  1942. {
  1943.     return sms != SN &&
  1944.            (sms->type == ST_CHILD || sms->type == ST_PEER) &&
  1945.            (sms->state == SS_RUNNING || sms->state == SS_CONNECT_WAIT ||
  1946.         sms->wait_id != 0L);
  1947. }
  1948.  
  1949. #if defined(X3270_MENUS) || defined(C3270) /*[*/
  1950. /* Return whether any scripts are active. */
  1951. Boolean
  1952. sms_active(void)
  1953. {
  1954.     return sms != SN;
  1955. }
  1956. #endif /*]*/
  1957.  
  1958. /* Translate an expect string (uses C escape syntax). */
  1959. static void
  1960. expand_expect(char *s)
  1961. {
  1962.     char *t = Malloc(strlen(s) + 1);
  1963.     char c;
  1964.     enum { XS_BASE, XS_BS, XS_O, XS_X } state = XS_BASE;
  1965.     int n = 0;
  1966.     int nd = 0;
  1967.     static char hexes[] = "0123456789abcdef";
  1968.  
  1969.     expect_text = t;
  1970.  
  1971.     while ((c = *s++)) {
  1972.         switch (state) {
  1973.             case XS_BASE:
  1974.             if (c == '\\')
  1975.                 state = XS_BS;
  1976.             else
  1977.                 *t++ = c;
  1978.             break;
  1979.             case XS_BS:
  1980.             switch (c) {
  1981.                 case 'x':
  1982.                 nd = 0;
  1983.                 n = 0;
  1984.                 state = XS_X;
  1985.                 break;
  1986.                 case 'r':
  1987.                 *t++ = '\r';
  1988.                 state = XS_BASE;
  1989.                 break;
  1990.                 case 'n':
  1991.                 *t++ = '\n';
  1992.                 state = XS_BASE;
  1993.                 break;
  1994.                 case 'b':
  1995.                 *t++ = '\b';
  1996.                 state = XS_BASE;
  1997.                 break;
  1998.                 default:
  1999.                 if (c >= '0' && c <= '7') {
  2000.                     nd = 1;
  2001.                     n = c - '0';
  2002.                     state = XS_O;
  2003.                 } else {
  2004.                     *t++ = c;
  2005.                     state = XS_BASE;
  2006.                 }
  2007.                 break;
  2008.             }
  2009.             break;
  2010.             case XS_O:
  2011.             if (nd < 3 && c >= '0' && c <= '7') {
  2012.                 n = (n * 8) + (c - '0');
  2013.                 nd++;
  2014.             } else {
  2015.                 *t++ = n;
  2016.                 *t++ = c;
  2017.                 state = XS_BASE;
  2018.             }
  2019.             break;
  2020.             case XS_X:
  2021.             if (isxdigit(c)) {
  2022.                 n = (n * 16) +
  2023.                     strchr(hexes, tolower(c)) - hexes;
  2024.                 nd++;
  2025.             } else {
  2026.                 if (nd) 
  2027.                     *t++ = n;
  2028.                 else
  2029.                     *t++ = 'x';
  2030.                 *t++ = c;
  2031.                 state = XS_BASE;
  2032.             }
  2033.             break;
  2034.         }
  2035.     }
  2036.     expect_len = t - expect_text;
  2037. }
  2038.  
  2039. /* 'mem' version of strstr */
  2040. static char *
  2041. memstr(char *s1, char *s2, int n1, int n2)
  2042. {
  2043.     int i;
  2044.  
  2045.     for (i = 0; i <= n1 - n2; i++, s1++)
  2046.         if (*s1 == *s2 && !memcmp(s1, s2, n2))
  2047.             return s1;
  2048.     return CN;
  2049. }
  2050.  
  2051. /* Check for a match against an expect string. */
  2052. static Boolean
  2053. expect_matches(void)
  2054. {
  2055.     int ix, i;
  2056.     unsigned char buf[ANSI_SAVE_SIZE];
  2057.     char *t;
  2058.  
  2059.     ix = (ansi_save_ix + ANSI_SAVE_SIZE - ansi_save_cnt) % ANSI_SAVE_SIZE;
  2060.     for (i = 0; i < ansi_save_cnt; i++) {
  2061.         buf[i] = ansi_save_buf[(ix + i) % ANSI_SAVE_SIZE];
  2062.     }
  2063.     t = memstr((char *)buf, expect_text, ansi_save_cnt, expect_len);
  2064.     if (t != CN) {
  2065.         ansi_save_cnt -= ((unsigned char *)t - buf) + expect_len;
  2066.         Free(expect_text);
  2067.         expect_text = CN;
  2068.         return True;
  2069.     } else
  2070.         return False;
  2071. }
  2072.  
  2073. /* Store an ANSI character for use by the Ansi action. */
  2074. void
  2075. sms_store(unsigned char c)
  2076. {
  2077.     if (sms == SN)
  2078.         return;
  2079.  
  2080.     /* Save the character in the buffer. */
  2081.     ansi_save_buf[ansi_save_ix++] = c;
  2082.     ansi_save_ix %= ANSI_SAVE_SIZE;
  2083.     if (ansi_save_cnt < ANSI_SAVE_SIZE)
  2084.         ansi_save_cnt++;
  2085.  
  2086.     /* If a script or macro is waiting to match a string, check now. */
  2087.     if (sms->state == SS_EXPECTING && expect_matches()) {
  2088.         RemoveTimeOut(sms->expect_id);
  2089.         sms->expect_id = 0L;
  2090.         sms->state = SS_INCOMPLETE;
  2091.         sms_continue();
  2092.     }
  2093. }
  2094.  
  2095. /* Dump whatever ANSI data has been sent by the host since last called. */
  2096. void
  2097. AnsiText_action(Widget w unused, XEvent *event unused, String *params unused,
  2098.     Cardinal *num_params unused)
  2099. {
  2100.     register int i;
  2101.     int ix;
  2102.     unsigned char c;
  2103.     char linebuf[ANSI_SAVE_SIZE * 4 + 1];
  2104.     char *s = linebuf;
  2105.  
  2106.     if (!ansi_save_cnt)
  2107.         return;
  2108.     ix = (ansi_save_ix + ANSI_SAVE_SIZE - ansi_save_cnt) % ANSI_SAVE_SIZE;
  2109.     for (i = 0; i < ansi_save_cnt; i++) {
  2110.         c = ansi_save_buf[(ix + i) % ANSI_SAVE_SIZE];
  2111.         if (!(c & ~0x1f)) switch (c) {
  2112.             case '\n':
  2113.             s += sprintf(s, "\\n");
  2114.             break;
  2115.             case '\r':
  2116.             s += sprintf(s, "\\r");
  2117.             break;
  2118.             case '\b':
  2119.             s += sprintf(s, "\\b");
  2120.             break;
  2121.             default:
  2122.             s += sprintf(s, "\\%03o", c);
  2123.             break;
  2124.         } else if (c == '\\')
  2125.             s += sprintf(s, "\\\\");
  2126.         else
  2127.             *s++ = (char)c;
  2128.     }
  2129.     *s = '\0';
  2130.     action_output(linebuf);
  2131.     ansi_save_cnt = 0;
  2132.     ansi_save_ix = 0;
  2133. }
  2134.  
  2135. /* Pause a script. */
  2136. void
  2137. PauseScript_action(Widget w unused, XEvent *event unused, String *params unused,
  2138.     Cardinal *num_params unused)
  2139. {
  2140.     if (sms == SN || (sms->type != ST_PEER && sms->type != ST_CHILD)) {
  2141.         popup_an_error("%s can only be called from a script",
  2142.             action_name(PauseScript_action));
  2143.         return;
  2144.     }
  2145.     sms->state = SS_PAUSED;
  2146. }
  2147.  
  2148. /* Continue a script. */
  2149. void
  2150. ContinueScript_action(Widget w, XEvent *event unused, String *params,
  2151.     Cardinal *num_params)
  2152. {
  2153.     if (check_usage(ContinueScript_action, *num_params, 1, 1) < 0)
  2154.         return;
  2155.  
  2156.     /*
  2157.      * If this is a nested script, this action aborts the current script,
  2158.      * then applies to the previous one.
  2159.      */
  2160.     if (w == (Widget)NULL && sms_depth > 1)
  2161.         sms_pop(False);
  2162.  
  2163.     /* Continue the previous script. */
  2164.     if (sms == SN || sms->state != SS_PAUSED) {
  2165.         popup_an_error("%s: No script waiting",
  2166.             action_name(ContinueScript_action));
  2167.         sms_continue();
  2168.         return;
  2169.     }
  2170.     action_output("%s", params[0]);
  2171.     sms->state = SS_RUNNING;
  2172.     sms_continue();
  2173. }
  2174.  
  2175. /* Stop listening to stdin. */
  2176. void
  2177. CloseScript_action(Widget w unused, XEvent *event unused, String *params,
  2178.     Cardinal *num_params)
  2179. {
  2180.     if (sms != SN &&
  2181.         (sms->type == ST_PEER || sms->type == ST_CHILD)) {
  2182.  
  2183.         /* Close this script. */
  2184.         sms->state = SS_CLOSING;
  2185.         script_prompt(True);
  2186.  
  2187.         /* If nonzero status passed, fail the calling script. */
  2188.         if (*num_params > 0 &&
  2189.             atoi(params[0]) != 0 &&
  2190.             sms->next != SN) {
  2191.             sms->next->success = False;
  2192.             if (sms->is_login)
  2193.                 host_disconnect(True);
  2194.         }
  2195.     } else
  2196.         popup_an_error("%s can only be called from a script",
  2197.             action_name(CloseScript_action));
  2198. }
  2199.  
  2200. /* Execute an arbitrary shell command. */
  2201. void
  2202. Execute_action(Widget w unused, XEvent *event unused, String *params,
  2203.     Cardinal *num_params)
  2204. {
  2205.     if (check_usage(Execute_action, *num_params, 1, 1) < 0)
  2206.         return;
  2207.     (void) system(params[0]);
  2208. }
  2209.  
  2210. /* Timeout for Expect action. */
  2211. static void
  2212. expect_timed_out(void)
  2213. {
  2214.     if (sms == SN || sms->state != SS_EXPECTING)
  2215.         return;
  2216.  
  2217.     Free(expect_text);
  2218.     expect_text = CN;
  2219.     popup_an_error("%s: Timed out", action_name(Expect_action));
  2220.     sms->expect_id = 0L;
  2221.     sms->state = SS_INCOMPLETE;
  2222.     sms->success = False;
  2223.     if (sms->is_login)
  2224.         host_disconnect(True);
  2225.     sms_continue();
  2226. }
  2227.  
  2228. /* Timeout for Wait action. */
  2229. static void
  2230. wait_timed_out(void)
  2231. {
  2232.     /* Pop up the error message. */
  2233.     popup_an_error("%s: Timed out", action_name(Wait_action));
  2234.  
  2235.     /* Forget the ID. */
  2236.     sms->wait_id = 0L;
  2237.  
  2238.     /* If this is a login macro, it has failed. */
  2239.     if (sms->is_login)
  2240.         host_disconnect(True);
  2241.  
  2242.     sms->success = False;
  2243.     sms->state = SS_INCOMPLETE;
  2244.  
  2245.     /* Let the script proceed. */
  2246.     sms_continue();
  2247. }
  2248.  
  2249. /* Wait for a string from the host (ANSI mode only). */
  2250. void
  2251. Expect_action(Widget w unused, XEvent *event unused, String *params,
  2252.     Cardinal *num_params)
  2253. {
  2254.     int tmo;
  2255.  
  2256.     /* Verify the environment and parameters. */
  2257.     if (sms == SN || sms->state != SS_RUNNING) {
  2258.         popup_an_error("%s can only be called from a script or macro",
  2259.             action_name(Expect_action));
  2260.         return;
  2261.     }
  2262.     if (check_usage(Expect_action, *num_params, 1, 2) < 0)
  2263.         return;
  2264.     if (!IN_ANSI) {
  2265.         popup_an_error("%s is valid only when connected in ANSI mode",
  2266.             action_name(Expect_action));
  2267.     }
  2268.     if (*num_params == 2) {
  2269.         tmo = atoi(params[1]);
  2270.         if (tmo < 1 || tmo > 600) {
  2271.             popup_an_error("%s: Invalid timeout: %s",
  2272.                 action_name(Expect_action), params[1]);
  2273.             return;
  2274.         }
  2275.     } else
  2276.         tmo = 30;
  2277.  
  2278.     /* See if the text is there already; if not, wait for it. */
  2279.     expand_expect(params[0]);
  2280.     if (!expect_matches()) {
  2281.         sms->expect_id = AddTimeOut(tmo * 1000, expect_timed_out);
  2282.         sms->state = SS_EXPECTING;
  2283.     }
  2284.     /* else allow sms to proceed */
  2285. }
  2286.  
  2287.  
  2288. #if defined(X3270_MENUS) /*[*/
  2289.  
  2290. /* "Execute an Action" menu option */
  2291.  
  2292. static Widget execute_action_shell = (Widget)NULL;
  2293.  
  2294. /* Callback for "OK" button on execute action popup */
  2295. static void
  2296. execute_action_callback(Widget w unused, XtPointer client_data,
  2297.     XtPointer call_data unused)
  2298. {
  2299.     char *text;
  2300.  
  2301.     text = XawDialogGetValueString((Widget)client_data);
  2302.     XtPopdown(execute_action_shell);
  2303.     if (!text)
  2304.         return;
  2305.     push_macro(text, False);
  2306. }
  2307.  
  2308. void
  2309. execute_action_option(Widget w unused, XtPointer client_data unused,
  2310.     XtPointer call_data unused)
  2311. {
  2312.     if (execute_action_shell == NULL)
  2313.         execute_action_shell = create_form_popup("ExecuteAction",
  2314.             execute_action_callback, (XtCallbackProc)NULL, FORM_NO_CC);
  2315.  
  2316.     popup_popup(execute_action_shell, XtGrabExclusive);
  2317. }
  2318.  
  2319. #endif /*]*/
  2320.  
  2321. /* "Script" action, runs a script as a child process. */
  2322. void
  2323. Script_action(Widget w unused, XEvent *event unused, String *params,
  2324.     Cardinal *num_params)
  2325. {
  2326.     int inpipe[2];
  2327.     int outpipe[2];
  2328.  
  2329.     if (*num_params < 1) {
  2330.         popup_an_error("%s requires at least one argument",
  2331.             action_name(Script_action));
  2332.         return;
  2333.     }
  2334.  
  2335.     /* Create a new script description. */
  2336.     if (!sms_push(ST_CHILD))
  2337.         return;
  2338.  
  2339.     /*
  2340.      * Create pipes and stdout stream for the script process.
  2341.      *  inpipe[] is read by x3270, written by the script
  2342.      *  outpipe[] is written by x3270, read by the script
  2343.      */
  2344.     if (pipe(inpipe) < 0) {
  2345.         sms_pop(False);
  2346.         popup_an_error("pipe() failed");
  2347.         return;
  2348.     }
  2349.     if (pipe(outpipe) < 0) {
  2350.         (void) close(inpipe[0]);
  2351.         (void) close(inpipe[1]);
  2352.         sms_pop(False);
  2353.         popup_an_error("pipe() failed");
  2354.         return;
  2355.     }
  2356.     if ((sms->outfile = fdopen(outpipe[1], "w")) == (FILE *)NULL) {
  2357.         (void) close(inpipe[0]);
  2358.         (void) close(inpipe[1]);
  2359.         (void) close(outpipe[0]);
  2360.         (void) close(outpipe[1]);
  2361.         sms_pop(False);
  2362.         popup_an_error("fdopen() failed");
  2363.         return;
  2364.     }
  2365.     (void) SETLINEBUF(sms->outfile);
  2366.  
  2367.     /* Fork and exec the script process. */
  2368.     if ((sms->pid = fork()) < 0) {
  2369.         (void) close(inpipe[0]);
  2370.         (void) close(inpipe[1]);
  2371.         (void) close(outpipe[0]);
  2372.         sms_pop(False);
  2373.         popup_an_error("fork() failed");
  2374.         return;
  2375.     }
  2376.  
  2377.     /* Child processing. */
  2378.     if (sms->pid == 0) {
  2379.         char **argv;
  2380.         Cardinal i;
  2381.         char env_buf[2][32];
  2382.  
  2383.         /* Clean up the pipes. */
  2384.         (void) close(outpipe[1]);
  2385.         (void) close(inpipe[0]);
  2386.  
  2387.         /* Export the names of the pipes into the environment. */
  2388.         (void) sprintf(env_buf[0], "X3270OUTPUT=%d", outpipe[0]);
  2389.         (void) putenv(env_buf[0]);
  2390.         (void) sprintf(env_buf[1], "X3270INPUT=%d", inpipe[1]);
  2391.         (void) putenv(env_buf[1]);
  2392.  
  2393.         /* Set up arguments. */
  2394.         argv = (char **)Malloc((*num_params + 1) * sizeof(char *));
  2395.         for (i = 0; i < *num_params; i++)
  2396.             argv[i] = params[i];
  2397.         argv[i] = CN;
  2398.  
  2399.         /* Exec. */
  2400.         (void) execvp(params[0], argv);
  2401.         (void) fprintf(stderr, "exec(%s) failed\n", params[0]);
  2402.         (void) _exit(1);
  2403.     }
  2404.  
  2405.     /* Clean up our ends of the pipes. */
  2406.     sms->infd = inpipe[0];
  2407.     (void) close(inpipe[1]);
  2408.     (void) close(outpipe[0]);
  2409.  
  2410.     /* Enable input. */
  2411.     script_enable();
  2412.  
  2413.     /* Set up to reap the child's exit status. */
  2414.     ++children;
  2415. }
  2416.  
  2417. /* "Macro" action, explicitly invokes a named macro. */
  2418. void
  2419. Macro_action(Widget w unused, XEvent *event unused, String *params,
  2420.     Cardinal *num_params)
  2421. {
  2422.     struct macro_def *m;
  2423.  
  2424.     if (check_usage(Script_action, *num_params, 1, 1) < 0)
  2425.         return;
  2426.     for (m = macro_defs; m != (struct macro_def *)NULL; m = m->next) {
  2427.         if (!strcmp(m->name, params[0])) {
  2428.             push_macro(m->action, False);
  2429.             return;
  2430.         }
  2431.     }
  2432.     popup_an_error("no such macro: '%s'", params[0]);
  2433. }
  2434.  
  2435. #if defined(X3270_PRINTER) /*[*/
  2436. /* "Printer" action, starts or stops a printer session. */
  2437. void
  2438. Printer_action(Widget w unused, XEvent *event unused, String *params,
  2439.     Cardinal *num_params)
  2440. {
  2441.     if (check_usage(Printer_action, *num_params, 1, 2) < 0)
  2442.         return;
  2443.     if (!strcasecmp(params[0], "Start")) {
  2444.         printer_start((*num_params > 1)? params[1] : CN);
  2445.     } else if (!strcasecmp(params[0], "Stop")) {
  2446.         if (*num_params != 1) {
  2447.             popup_an_error("%s: Extra argument(s)",
  2448.                 action_name(Printer_action));
  2449.             return;
  2450.         }
  2451.         printer_stop();
  2452.     } else {
  2453.         popup_an_error("%s: Argument must Start or Stop",
  2454.             action_name(Printer_action));
  2455.     }
  2456. }
  2457. #endif /*]*/
  2458.  
  2459. #if defined(X3270_MENUS) /*[*/
  2460. /* Abort all running scripts. */
  2461. void
  2462. abort_script(void)
  2463. {
  2464.     while (sms != SN) {
  2465.         if (sms->type == ST_CHILD && sms->pid > 0)
  2466.             (void) kill(sms->pid, SIGTERM);
  2467.         sms_pop(True);
  2468.     }
  2469. }
  2470. #endif /*]*/
  2471.